import random
import J_Fast_Sort
import numpy as np
from F_Population import *
import A_Choose_Util

from G_Initial import *
from C_Load_Process_Map import *


# 种群更新操作方法
# 个体的交叉 变异 


# 确认种群中最优解 和 最差解
# 方法：根据拥挤距离定义最优和最差
# 最优：第一层拥挤度最大个体   最差：最后一层拥挤度最小个体
# 注意： 拥挤度  ---> 更改过的拥挤度 通过染色体出现次数进行计算
# 输入：种群
def Determina_Worse_Best(Pop):
    Nodominate = Pop.Nodominate_Ship  # 得到种群中的支配关系
    Di_Best = J_Fast_Sort.Modified_Crowding_Distance(Nodominate[0], Pop.Population_Member)  # 拥挤度距离
    Di_Worse = J_Fast_Sort.Modified_Crowding_Distance(Nodominate[len(Nodominate) - 1], Pop.Population_Member)
    Best_Idv_Index = A_Choose_Util.Get_Max_Index(Di_Best)
    Worse_Idv_Index = A_Choose_Util.Get_Min_Index(Di_Worse)
    Best_Idv = Pop.Population_Member[Nodominate[0][Best_Idv_Index]]
    Worse_Idv = Pop.Population_Member[Nodominate[len(Nodominate) - 1][Worse_Idv_Index]]
    return Best_Idv, Worse_Idv


# DJaya 种群更新
# 输入：当前种群
# 输出：更新后种群
def Update_Pop_DJaya(Pop, Map):
    print("模块：GA_Util\t方法：Update_Pop_DJaya(Pop,Map):")
    New_Pop = Population()  # 新种群 防止原始种群被破坏
    New_Pop.Update_Pop(Pop.Population_Member)  # 个体赋值操作
    Best_Idv, Worse_Idv = Determina_Worse_Best(Pop)
    # 将全部个体进行更新
    for i in range(New_Pop.Pop_Size):
        if New_Pop.Population_Member[i] != Best_Idv and New_Pop.Population_Member[i] != Worse_Idv:
            New_Job_Chromosome, New_Machine_Chromosome = Cross_Dyaja(New_Pop.Population_Member[i], Best_Idv, Worse_Idv)
            New_Pop.Population_Member[i].Update_Chromosome(New_Job_Chromosome, New_Machine_Chromosome, Map)
    New_Pop.Determine_Nodominate_Ship()  # 更新新种群的关系信息
    return New_Pop


# DJaya 交叉方式 OS
# 参考最好的  远离最差的
# 输入：需要更新的个体 种群最优个体  种群最差个体
# 输出：新的个体
def Cross_Dyaja(P_Idv, B_Idv, W_Idv):
    print("模块：GA_Util\t方法：Cross_Dyaja(P_Idv,B_Idv,W_Idv):")
    Chromosome_Length = len(P_Idv.Job_Chromosome)  # 染色体长度
    Sign_Variable = []  # 决策变量 1 表示和Worse个体一致的基因位
    Job_Operator = [1] * max(P_Idv.Job_Chromosome)  # 工序号
    Unchanged_Operator = []  # 记录不变的工序位
    New_Job_Chromosome = [0] * Chromosome_Length  # 新OS染色体
    New_Machine_Chromosome = [0] * Chromosome_Length  # 新MS染色体
    Changed_Machine_Index = []  # 需要改变的基因位

    # 从最差确定决策变量和不动的基因位置 
    for i in range(Chromosome_Length):
        if P_Idv.Job_Chromosome[i] == W_Idv.Job_Chromosome[i]:
            Sign_Variable.append(i)  # 需要变动的基因位置
        else:
            New_Job_Chromosome[i] = P_Idv.Job_Chromosome[i]
            Temp = []
            Temp.append(P_Idv.Job_Chromosome[i])
            Temp.append(Job_Operator[P_Idv.Job_Chromosome[i] - 1])
            Job_Operator[P_Idv.Job_Chromosome[i] - 1] += 1
            Unchanged_Operator.append(Temp)  # [[2,1],...,]

        if P_Idv.Machine_Chromosome[i] == W_Idv.Machine_Chromosome[i]:
            Changed_Machine_Index.append(i)
        else:
            New_Machine_Chromosome[i] = P_Idv.Machine_Chromosome[i]

    Job_Operator = [1] * max(P_Idv.Job_Chromosome)  # 重新记录
    Index = 0  # 标记需要更新的基因位
    # 从最优个体确定剩余基因位
    for i in range(Chromosome_Length):
        Temp = []
        Temp.append(B_Idv.Job_Chromosome[i])
        Temp.append(Job_Operator[B_Idv.Job_Chromosome[i] - 1])
        Job_Operator[B_Idv.Job_Chromosome[i] - 1] += 1
        if Temp not in Unchanged_Operator:  # 最优个体的基因位
            New_Job_Chromosome[Sign_Variable[Index]] = B_Idv.Job_Chromosome[i]
            Index += 1

    # 直接将最优个体的基因片段加入
    for i in Changed_Machine_Index:
        New_Machine_Chromosome[i] = B_Idv.Machine_Chromosome[i]
    return New_Job_Chromosome, New_Machine_Chromosome


# 功能函数
# 计算每台机器上的负载 以及 加工工序
def Get_Machine_Process(Idv, Map):
    print("模块：GA_Util\t方法：Get_Machine_Process(Idv,Map):")
    Machine_Workload = [0] * Map.Machine_Num  # 确定每台机器的负载
    Machine_Assignment = [[] for _ in range(Map.Machine_Num)]  # 每台机器的工序分配
    Machine_Assignment_Time = [[] for _ in range(Map.Machine_Num)]  # 每台机器的工序加工时间

    Index = 0  # 标记MS染色体下标
    for i in range(Map.Job_Num):  # 每个工件
        for j in range(Map.Operation_Num[i]):  # 此工件的每个工序
            Machine_Index = Map.Operation_Accessible_Machine_Index[i][j].index(
                Idv.Machine_Chromosome[Index] - 1)  # 加工机器下标
            Machine_Workload[Idv.Machine_Chromosome[Index] - 1] += Map.Operation_Accessible_Machine_Time[i][j][
                Machine_Index]  # 加工时间
            Temp = []  # [工件号,工序号]  不加1 下面计算Ratio 还需要使用
            Temp.append(i)  # 工件号
            Temp.append(j)  # 工序号
            Machine_Assignment[Idv.Machine_Chromosome[Index] - 1].append(Temp)
            Machine_Assignment_Time[Idv.Machine_Chromosome[Index] - 1].append(
                Map.Operation_Accessible_Machine_Time[i][j][Machine_Index])
            Index += 1
    return Machine_Workload, Machine_Assignment, Machine_Assignment_Time


# 变异更新种群  跳出局部最优
# 对象：个体一样   目标值一样两个个体  
# 输入：种群 加工图 变异概率
# 输出：更新后种群
def Mutation_Pop_DJaya(Pop, Map, Prop):
    print("模块：GA_Util\t方法：Mutation_Pop_DJaya(Pop,Map,Prop):")
    Same_Idv_Index = []  # 相同个体下标 [[,...,],...,]
    Same_Obj_Index = []  # 相同目标值个体下标 [[,...,],...,]

    Sign_Idv = [0] * Pop.Pop_Size  # 决策变量 判断此元素是否被遍历到 1表示访问到
    Sign_Obj = [0] * Pop.Pop_Size  # 决策变量 判断此元素是否被遍历到
    # 找个体一样的
    while 0 in Sign_Idv:  # 确保每个个体都访问到
        for i in range(Pop.Pop_Size):
            if Sign_Idv[i] == 0:  # 寻找未访问的个体
                Temp = []
                Temp.append(i)  # 先把第一个个体加入
                Sign_Idv[i] = 1  # 标记访问
                for j in range(Pop.Pop_Size):
                    if Sign_Idv[j] == 0:
                        if (Pop.Population_Member[i].Job_Chromosome.tolist() == Pop.Population_Member[
                            j].Job_Chromosome.tolist()
                                and Pop.Population_Member[i].Machine_Chromosome.tolist() == Pop.Population_Member[
                                    j].Machine_Chromosome.tolist()):
                            Temp.append(j)
                            Sign_Idv[j] = 1  # 标记访问
                if len(Temp) >= 2:
                    Same_Idv_Index.append(Temp)

                    # 找目标值相同个体
    while 0 in Sign_Obj:  # 确保每个个体都访问到
        for i in range(Pop.Pop_Size):
            if Sign_Obj[i] == 0:  # 寻找未访问的个体
                Temp = []
                Temp.append(i)  # 先把第一个个体加入
                Sign_Obj[i] = 1  # 标记访问
                for j in range(Pop.Pop_Size):
                    if Sign_Obj[j] == 0:
                        if Pop.Population_Member[i].Adaptability == Pop.Population_Member[j].Adaptability:
                            Temp.append(j)
                            Sign_Obj[j] = 1  # 标记访问
                if len(Temp) >= 2:
                    Same_Obj_Index.append(Temp)

    if len(Same_Idv_Index) != 0:  # 遍历每一个重复个体 保留一个其余以概率变异
        for i in range(len(Same_Idv_Index)):
            for j in range(1, len(Same_Idv_Index[i])):  # 保留第一个
                R_Random = random.uniform(0, 1)
                if R_Random <= Prop:
                    M_Idv = Maximum_Workload_Reduction(Pop.Population_Member[Same_Idv_Index[i][j]], Map)
                    J_Idv = Reverse_Operation_Mutation(M_Idv, Map)
                    Pop.Population_Member[Same_Idv_Index[i][j]] = J_Idv

    if len(Same_Obj_Index) != 0:  # 遍历每一个重复个体 保留一个其余以概率变异
        for i in range(len(Same_Obj_Index)):
            for j in range(1, len(Same_Obj_Index[i])):  # 保留第一个
                R_Random = random.uniform(0, 1)
                if R_Random <= Prop:
                    M_Idv = Maximum_Workload_Reduction(Pop.Population_Member[Same_Obj_Index[i][j]], Map)
                    J_Idv = Reverse_Operation_Mutation(M_Idv, Map)
                    Pop.Population_Member[Same_Obj_Index[i][j]] = J_Idv


# MS变异 
# 挑选最大负载机器上的一个工序  计算  加工时间/工序序号  找到max的调整到最小负载机器
# 输入：个体
# 输出：更新后个体
def Maximum_Workload_Reduction(Idv, Map):
    print("模块：GA_Util\t方法：Maximum_Workload_Reduction(Idv,Map):")
    Machine_Workload, Machine_Assignment, Machine_Assignment_Time = Get_Machine_Process(Idv, Map)
    Max_Workload_Machine_Index = A_Choose_Util.Get_Max_Index(Machine_Workload)  # 最大负载机器下标
    Processed_Operator = Machine_Assignment[Max_Workload_Machine_Index]  # 在此机器上加工的工序集合
    Processed_Operator_Num = len(Processed_Operator)  # 在此机器上加工的工序数量
    Ratio = [0] * Processed_Operator_Num  # Ratio值挑选最大的
    for i in range(Processed_Operator_Num):  # 遍历此机器上每个工序
        Ratio[i] = Machine_Assignment_Time[Max_Workload_Machine_Index][i] / (Processed_Operator[i][1] + 1)
    Choose_Operator = Processed_Operator[A_Choose_Util.Get_Max_Index(Ratio)]  # 挑选中的工序  准备转移加工机器的工序
    Choose_Operator_Map_Index = Map.Operation_Accessible_Machine_Index[Choose_Operator[0]][Choose_Operator[1]]
    # 挑选最小负载机器
    Min_Workload_Machine = []
    Min_Workload_Machine_Index = []

    for i in range(len(Choose_Operator_Map_Index)):
        Min_Workload_Machine.append(Machine_Workload[Choose_Operator_Map_Index[i]])
        Min_Workload_Machine_Index.append(Choose_Operator_Map_Index[i])
    _, _, Choose_Index = A_Choose_Util.Get_Min_Info_With(Min_Workload_Machine, Min_Workload_Machine_Index)
    Temp_Machine_Index = 0  # 确定更新的基因位的下标
    # 更新MS染色体
    for i in range(Choose_Operator[0]):  # 算上前面工件
        Temp_Machine_Index += Map.Operation_Num[i]
    Temp_Machine_Index += Choose_Operator[1]  # 此工件
    Idv.Machine_Chromosome[Temp_Machine_Index] = Choose_Index + 1
    # 更新个体
    New_Idv = Individual()
    New_Idv.Update_Chromosome(Idv.Job_Chromosome, Idv.Machine_Chromosome, Map)
    return New_Idv


# OS变异 反转染色体变异
# 挑选两个基因位  中间的染色体全部反转
# 输入：个体
# 输出：更新后个体
def Reverse_Operation_Mutation(Idv, Map):
    print("模块：GA_Util\t方法：Reverse_Operation_Mutation(Idv,Map):")
    # 产生随机位置 确保执行操作
    Random_Index1 = np.random.randint(0, Map.Chromosome_Length)
    Random_Index2 = np.random.randint(0, Map.Chromosome_Length)
    while Random_Index1 == Random_Index2:
        Random_Index2 = np.random.randint(0, Map.Chromosome_Length)
        # 确定左右位置
    Left_Pos = Random_Index1 if Random_Index1 < Random_Index2 else Random_Index2
    Right_Pos = Random_Index1 if Random_Index1 > Random_Index2 else Random_Index2
    Temp_List = []  # 需要反转的基因位
    for i in range(Left_Pos, Right_Pos + 1):
        Temp_List.append(Idv.Job_Chromosome[i])
    Temp_List.reverse()  # 反转
    for i in range(len(Temp_List)):
        Idv.Job_Chromosome[Left_Pos] = Temp_List[i]
        Left_Pos += 1
    New_Idv = Individual()
    New_Idv.Update_Chromosome(Idv.Job_Chromosome, Idv.Machine_Chromosome, Map)
    return New_Idv


# 获得变异概率
# 输入：代数
# 输出：变异概率
def Get_Mutation_Prob(Iteration, Max_Iteration):
    print("模块：GA_Util\t方法：Get_Mutation_Prob(Iteration,Max_Iteration):")
    R_Max = 0.8
    R_Min = 0.2
    return R_Max - (((R_Max - R_Min) / Max_Iteration) * Iteration)


if __name__ == "__main__":
    aaa = Individual()
    print(aaa == None)